home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockPhotoUploadService.js < prev    next >
Text File  |  2007-10-12  |  37KB  |  1,027 lines

  1. //
  2. // BEGIN FLOCK GPL
  3. // 
  4. // Copyright Flock Inc. 2005-2007
  5. // http://flock.com
  6. // 
  7. // This file may be used under the terms of of the
  8. // GNU General Public License Version 2 or later (the "GPL"),
  9. // http://www.gnu.org/licenses/gpl.html
  10. // 
  11. // Software distributed under the License is distributed on an "AS IS" basis,
  12. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. // for the specific language governing rights and limitations under the
  14. // License.
  15. // 
  16. // END FLOCK GPL
  17. //
  18.  
  19. const Cc = Components.classes;
  20. const Ci = Components.interfaces;
  21. const Cr = Components.results;
  22.  
  23. const FLOCK_PHOTO_UPLOAD_CID        = Components.ID('{9d499deb-fd01-43d5-9eda-91783a37f3bc}');
  24. const FLOCK_PHOTO_UPLOAD_FNAME      = "flock_photo_uploads.rdf";
  25.  
  26. const FLOCK_PHOTO_UPLOADS_CONTRACTID = '@mozilla.org/rdf/datasource;1?name=flock-photo-uploads';
  27. const LOCAL_FILE_CONTRACTID         = '@mozilla.org/file/local;1';
  28. const PREFERENCES_CONTRACTID        = '@mozilla.org/preferences-service;1';
  29. const IO_SERVICE_CONTRACTID         = '@mozilla.org/network/io-service;1';
  30. const FLOCK_ERROR_CONTRACTID        = "@flock.com/error;1";
  31.  
  32. const FLOCK_NS                      = 'http://flock.com/rdf#';
  33. const FLOCK_RDF_UPLOAD_ROOT         = 'urn:flock:photo:upload';
  34.  
  35. function flock_getImageUtils() {
  36.   return Cc['@flock.com/imagescaler;1'].createInstance(Ci.flockIImageScaler);
  37. }
  38.  
  39. function flockPhotoUploadService() {
  40.   var profd = Cc['@mozilla.org/file/directory_service;1'].getService(Ci.nsIProperties).get('ProfD', Ci.nsIFile);
  41.   var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
  42.   try {
  43.     file.initWithPath(profd.path);
  44.   }
  45.   catch(e) {
  46.     var tmpd = Components.classes['@mozilla.org/file/directory_service;1'].
  47.       getService(Ci.nsIProperties).get('TmpD', Ci.nsIFile);
  48.     file.initWithPath(tmpd.path);
  49.   }
  50.  
  51.   file.append(FLOCK_PHOTO_UPLOAD_FNAME);
  52.   // if(!file.exists()) file.createUnique(0,0600);
  53.  
  54.   var ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
  55.   var fileHandler = ios.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler);
  56.   var spec = fileHandler.getURLSpecFromFile(file);
  57.  
  58.   this.listeners = Array();
  59.   this.ds = new RDFDataSource(spec);
  60.   this.dataSource = this.ds.getRawDataSource().QueryInterface (Ci.nsIRDFRemoteDataSource);
  61.   this.uploadRoot = this.ds.getNode(FLOCK_RDF_UPLOAD_ROOT);
  62.   this.uploadRoot.makeSeq();
  63.  
  64.   this.Flush();
  65.  
  66.   var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  67.   obs.addObserver(this, 'flock-data-ready', false);
  68.  
  69.   var inst = this;
  70.   this.mTimerFunc = {
  71.     notify: function(aTimer) {
  72.       inst.Flush();
  73.     }
  74.   }
  75.   this.mTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  76.   this.running = false;
  77.   this._isImporting = false;
  78.   this.mImportQueue = Array();
  79.   this.mAsyncCopier = Cc["@mozilla.org/network/async-stream-copier;1"].createInstance(Ci.nsIAsyncStreamCopier);
  80.   this.videoUtil = {}; //Components.classes[FLOCK_VIDEOUTIL_CONTRACTID].createInstance(Components.interfaces.flockIVideoUtil);
  81. }
  82.  
  83.  
  84. flockPhotoUploadService.prototype.init = function PUS_init () {
  85.   // Logger
  86.   this.logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  87.   this.logger.init("photoUploadService");
  88.  
  89.   // Prepare the data 
  90.   this._coop = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton).getSingleton('chrome://browser/content/flock/common/load-faves-coop.js').wrappedJSObject;
  91. }
  92.  
  93.  
  94. flockPhotoUploadService.prototype.doSave = function(aListener) {
  95.   this.mTimer.cancel();
  96.   this.mTimer.initWithCallback(this.mTimerFunc,2*1000,0);//re-check token
  97. }
  98.  
  99.  
  100. flockPhotoUploadService.prototype.addListener = function(aListener) {
  101.   this.listeners.push(aListener);
  102.   debug("adding photo upload listener " + this.listeners.length + "\n");
  103. }
  104.  
  105.  
  106. flockPhotoUploadService.prototype.removeListener = function(aListener) {
  107.   for(var i=0;i<this.listeners.length;++i) {
  108.     if(aListener==this.listeners[i]) {
  109.       this.listeners.splice(i,1);
  110.       break;
  111.     }
  112.   }
  113. }
  114.  
  115.  
  116. flockPhotoUploadService.prototype.hasOnePhotoAccount = function PUS_hasOnePhotoAccount () {
  117.   var haveAccount = false;
  118.   // Iterate through all accounts, looking for photo upload accounts.
  119.   // Separate them according to auth state
  120.   var accountsEnum = this._coop.accounts_root.children.enumerate();
  121.   while (accountsEnum.hasMoreElements()) {
  122.     var acctCoopObj = accountsEnum.getNext();
  123.     try {
  124.       var svc = Cc[acctCoopObj.serviceId].getService(Ci.flockIWebService);
  125.       svc.QueryInterface(Components.interfaces.flockIPhotoAPI);
  126.       var acct = svc.getAccount(acctCoopObj.id());
  127.       acct.QueryInterface(Components.interfaces.flockIMediaUploadAccount);
  128.       haveAccount = true;
  129.     } catch (ex) {
  130.       // This account is not a flockIMediaUploadAccount
  131.     }
  132.   }
  133.   
  134.   return haveAccount; 
  135. }
  136.  
  137.  
  138. flockPhotoUploadService.prototype.promptForAccount = function PUS_promptForAccount () {
  139.   var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
  140.   var win = wm.getMostRecentWindow('navigator:browser');
  141.   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  142.                   .getService(Components.interfaces.nsIPromptService);
  143.   if (promptService.confirm(win, "Photo Account Required", "A Photo Account is required to upload photos. Click OK to open My Accounts where you can join a photo service or add an existing account to Flock.")) {
  144.     win.flock_photo.util.launchSettings(null);
  145.   }
  146. }
  147.  
  148.  
  149. flockPhotoUploadService.prototype._showUploaderWindow = function PUS__showUploaderWindow () {
  150.   // Set pref that specifies whether the uploader has been used
  151.   var prefService = Cc['@mozilla.org/preferences-service;1'].getService(Ci.nsIPrefService);
  152.   prefService.getBranch("flock.photo.uploader.").setBoolPref("firstruncomplete", true);
  153.   
  154.   var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
  155.   var uploaderWindow = wm.getMostRecentWindow("Flock:PhotoUploader");
  156.   var win = wm.getMostRecentWindow('navigator:browser');
  157.   if (uploaderWindow) {
  158.     uploaderWindow.focus();
  159.   } else {
  160.     win.open("chrome://browser/content/flock/photo/photoUploader.xul",
  161.              "PhotoUploader",
  162.              "chrome=1,resizable=1,width=680,height=660");
  163.   }
  164. }
  165.  
  166.  
  167. flockPhotoUploadService.prototype.launchUploader = function PUS_launchUploader () {
  168.   // Tell the user if he has to configure an account
  169.   if (this.hasOnePhotoAccount())
  170.     this._showUploaderWindow();
  171.   else
  172.     this.promptForAccount();
  173. }
  174.  
  175.  
  176. flockPhotoUploadService.prototype.getDefaultService = function() {
  177.   var prefService = Cc['@mozilla.org/preferences-service;1'].getService(Ci.nsIPrefService);
  178.   try {
  179.     var pref = prefService.getBranch("flock.photo.").getCharPref("defaultUploadService");
  180.     if (pref && pref.length)
  181.       return pref;
  182.     return null;
  183.   } catch(e) {
  184.     return null;
  185.   }
  186. }
  187.  
  188. flockPhotoUploadService.prototype.setDefaultService = function(aServiceId) {
  189.   var prefService = Cc['@mozilla.org/preferences-service;1'].getService(Ci.nsIPrefService);
  190.   prefService.getBranch("flock.photo.").setCharPref("defaultUploadService", aServiceId);
  191. }
  192.  
  193.  
  194.  
  195. flockPhotoUploadService.prototype.save = function (aListener) {
  196.   this.Flush();
  197. }
  198.  
  199.  
  200. flockPhotoUploadService.prototype.getURI = function (aURI) {
  201.   return aURI;
  202. }
  203.  
  204. flockPhotoUploadService.prototype.dictionary2Params = function (aDictionary) {
  205.   var params = {};
  206.   var obj = {};
  207.   var count = {};
  208.   var keys = aDictionary.getKeys(count, obj);
  209.   for(var i=0;i<keys.length;++i) {
  210.     var supports = aDictionary.getValue(keys[i]);
  211.     var supportsString = supports.QueryInterface(Ci.nsISupportsString);
  212.     var val = supportsString.toString();
  213.     params[keys[i]] = val;
  214.   }
  215.   return params;
  216. }
  217.  
  218.  
  219. flockPhotoUploadService.prototype.cancelUpload = function () {
  220.   this.uploadListener.stop();
  221. }
  222.  
  223. flockPhotoUploadService.prototype.__defineGetter__('isUploading', function () { return this.running; })
  224. flockPhotoUploadService.prototype.__defineGetter__('isImporting', function () { return this._isImporting; })
  225.  
  226. flockPhotoUploadService.prototype.singleUpload = function(aListener, aAPI, aUpload) {
  227.   var url = this.doTransform(aUpload);
  228.   aAPI.upload2(aListener, aUpload, url);// note that this doesn't use the parameter stuff
  229.   return;
  230. }
  231.  
  232. flockPhotoUploadService.prototype.addMediaToImportQueue = function (aUrl) {
  233.   this.addToQueue(aUrl);
  234. }
  235.  
  236. flockPhotoUploadService.prototype.importSingleMediaItem = function(aListener, aUrl) {
  237.   this._isImporting = true;
  238.   var inst = this;
  239.   var listener = {
  240.     onCreateStart: function(aTitle) {
  241.       aListener.onMediaImportStart(aTitle, 1);
  242.     },
  243.     
  244.     onCreateEnd: function(aPhotoUpload) {
  245.       inst._isImporting = false;
  246.       aListener.onMediaImportFinish(aPhotoUpload, 1);
  247.     },
  248.     
  249.     onCreateError: function(aFilename) {
  250.       inst._isImporting = false;
  251.       aListener.onMediaImportError(aFilename);
  252.     }
  253.    }
  254.    this.createPhotoUpload(listener, aUrl);
  255.   },
  256.  
  257. flockPhotoUploadService.prototype.addToQueue = function (aUrl) {
  258.   this.mImportQueue.push(aUrl);
  259. }
  260.  
  261. flockPhotoUploadService.prototype.startImportQueue = function (aListener) {
  262.   var inst = this;
  263.   
  264.   var processor = {
  265.     count: 0,
  266.     start: function() {
  267.       for(var i=0;i<inst.listeners.length;++i) {
  268.         inst.listeners[i].onMediaBatchImportStart(inst.mImportQueue.length);
  269.       }
  270.       this.next();
  271.     },
  272.     
  273.     next: function() {
  274.       if (inst.mImportQueue.length == 0) this.finish();
  275.       this.count++;
  276.       var url = inst.mImportQueue.shift();
  277.       try {
  278.         inst.createPhotoUpload(this, url);
  279.       } catch (ex) {
  280.         this.onCreateError(url);
  281.       }
  282.     },
  283.     
  284.     onCreateStart: function(aTitle) {
  285.       for(var i=0;i<inst.listeners.length;++i) {
  286.         inst.listeners[i].onMediaImportStart(aTitle, this.count);
  287.       }  
  288.     },
  289.     
  290.     onCreateEnd: function(aMediaUpload) {
  291.       for(var i=0;i<inst.listeners.length;++i) {
  292.         inst.listeners[i].onMediaImportFinish(aMediaUpload, this.count);
  293.       }
  294.      this.next();      
  295.     },
  296.     
  297.     onCreateError: function(aFilename) {
  298.       for(var i=0;i<inst.listeners.length;++i) {
  299.         inst.listeners[i].onMediaImportError(aFilename);
  300.       }  
  301.     },
  302.  
  303.     onError: function(aError) {
  304.       for(var i=0;i<inst.listeners.length;++i) {
  305.         inst.listeners[i].onError(aError);
  306.       }  
  307.     },
  308.         
  309.     finish: function() {
  310.       inst._isImporting = false;
  311.       for(var i=0;i<inst.listeners.length;++i) {
  312.         inst.listeners[i].onMediaBatchImportFinish();
  313.       }
  314.     }
  315.   }
  316.   
  317.   this._isImporting = true;
  318.   processor.start();
  319. }
  320.  
  321. flockPhotoUploadService.prototype.cancelImport = function () {
  322.   //this.mAsyncCopier.cancel(null);
  323.   this._isImporting = false;
  324.   this.mImportQueue = Array(); // flush the array
  325.   // signal we're done
  326.   for(var i=0;i<this.listeners.length;++i) {
  327.     this.listeners[i].onMediaBatchImportFinish();
  328.   }  
  329. }
  330.  
  331.  
  332. flockPhotoUploadService.prototype.getUniqueFname = function(aFName) {
  333.   aFName.match(/.+\.(.+)/);
  334.   var ext = RegExp.$1;
  335.   if(!ext) ext = "";
  336.   var now = new Date();
  337.   var sinkFileName = hex_md5(aFName + now + flock_photo.util.callCount++);
  338.   sinkFileName = sinkFileName + "." + ext;
  339.   return sinkFileName;
  340. }
  341.  
  342. function getOtherFName(aFName, aType) {
  343.   aFName.match(/.+\.(.+)/);
  344.   var ext = RegExp.$1;
  345.   debug(aFName + "." + aType + "." + ext + "\n");
  346.   return aFName + "." + aType + "." + ext;
  347. }
  348.  
  349. flockPhotoUploadService.prototype.cropPhotoUpload = function (aPhotoUpload, aX1, aY1, aX2, aY2) {
  350.   var rotation = parseInt(aPhotoUpload.rotation);
  351.   var cropString = aPhotoUpload.crop;
  352.   var coords = cropString.split(",");
  353.   var x1, y1, x2, y2;
  354.   x1 = parseFloat(coords[0]);
  355.   y1 = parseFloat(coords[1]);
  356.   x2 = parseFloat(coords[2]);
  357.   y2 = parseFloat(coords[3]);
  358.  
  359.   // rotate coords back to coordinate system of the original photo
  360.   function rotateLeftNinetyDegrees() {
  361.     var tmp = aX1;
  362.     aX1 = aY1;
  363.     aY1 = 1 - aX2;
  364.     aX2 = aY2;
  365.     aY2 = 1 - tmp;
  366.   }
  367.  
  368.   var tmp = rotation;
  369.   while(tmp%360!=0) {
  370.     rotateLeftNinetyDegrees();
  371.     tmp -= 90;
  372.   }
  373.  
  374.   var oldW = x2 - x1;
  375.   var oldH = y2 - y1;
  376.  
  377.   aX1 = x1 + oldW * aX1;
  378.   aX2 = x1 + oldW * aX2;
  379.  
  380.   aY1 = y1 + oldH * aY1;
  381.   aY2 = y1 + oldH * aY2;
  382.  
  383.   this.regenerateThumbs(aPhotoUpload, rotation, aX1, aY1, aX2, aY2);
  384. }
  385.  
  386. flockPhotoUploadService.prototype.revertPhotoUpload = function (aPhotoUpload) {
  387.   this.regenerateThumbs(aPhotoUpload,0,0,0,1,1);
  388. }
  389.  
  390. flockPhotoUploadService.prototype.doCrop= function (aPhotoUpload, aDegrees, aSize) {
  391.   var originalPath = aPhotoUpload.originalFilePath;
  392.   var workingPath = aPhotoUpload.workingFilePath;
  393.   var newWorkingFileName = flock_photo.util.getUniqueFname(workingPath);
  394.   var newWorkingFile = flock_getTmpFile(newWorkingFileName);
  395.   var cropString = aPhotoUpload.crop;
  396.  
  397.   var rotation = parseInt(aPhotoUpload.rotation);
  398.   var degrees = rotation;
  399.   var size = null;
  400.  
  401.   if (aDegrees != null) degrees = aDegrees;
  402.   if (aSize != null) size = aSize;
  403.   // do the crop
  404.   if ((cropString == "0,0,1,1") && (aDegrees == 0)) {
  405.     flock_photo.util.resize(size, originalPath, newWorkingFile.path);
  406.   }
  407.   else {
  408.     var coords = cropString.split(",");
  409.     var x1, y1, x2, y2;
  410.     x1 = parseFloat(coords[0]);
  411.     y1 = parseFloat(coords[1]);
  412.     x2 = parseFloat(coords[2]);
  413.     y2 = parseFloat(coords[3]);
  414.  
  415.  
  416.     var origDims = flock_photo.util.getDimensions(originalPath);
  417.  
  418.     var x = Math.floor(x1 * origDims.x);
  419.     var y = Math.floor(y1 * origDims.y);
  420.     var w = Math.ceil((x2 - x1) * origDims.x);
  421.     var h = Math.ceil((y2 - y1) * origDims.y);
  422.     flock_photo.util.cropExt(originalPath,
  423.                       newWorkingFile.path,
  424.                       w,
  425.                       h,
  426.                       x,
  427.                       y,
  428.                       degrees,
  429.                       size,
  430.                       origDims.px,
  431.                       origDims.py
  432.                       );
  433.   }
  434.  
  435.   // create the working file
  436.   return newWorkingFile;
  437. }
  438.  
  439. flockPhotoUploadService.prototype.regenerateThumbs = function (aPhotoUpload, aDegrees, aX1, aY1, aX2, aY2) {
  440.   if(aX1!=null) {
  441.     aPhotoUpload.crop = aX1 + "," + aY1 + "," + aX2 + "," + aY2;
  442.     var newWorkingFile = this.doCrop(aPhotoUpload, 0, FLOCK_PREVIEW_SIZE);
  443.     var newWorkingSpec = flock_photo.util.getURLFromFile(newWorkingFile);
  444.     flock_photo.util.removeFileByPath(aPhotoUpload.workingFilePath);
  445.     aPhotoUpload.workingFilePath = newWorkingFile.path;
  446.     aPhotoUpload.workingFileSpec = newWorkingSpec;
  447.   }
  448.  
  449.   var originalPath = aPhotoUpload.originalFilePath;
  450.   var previewPath = aPhotoUpload.previewFilePath;
  451.   var thumbPath = aPhotoUpload.thumbFilePath;
  452.   var workingPath = aPhotoUpload.workingFilePath;
  453.  
  454.   var rotation = aDegrees;
  455.   var newPreviewFileName = flock_photo.util.getUniqueFname(previewPath);
  456.   var newPreviewFile = flock_getTmpFile(newPreviewFileName);
  457.   flock_photo.util.rotate(rotation, aPhotoUpload.workingFilePath, newPreviewFile.path);
  458.  
  459.   var newPreviewSpec = flock_photo.util.getURLFromFile(newPreviewFile);
  460.  
  461.   var newThumbFileName = flock_photo.util.getUniqueFname(thumbPath);
  462.   var newThumbFile = flock_getTmpFile(newThumbFileName);
  463.   flock_photo.util.thumb(FLOCK_THUMB_SIZE, newPreviewFile.path, newThumbFile.path);
  464.   var newThumbSpec = flock_photo.util.getURLFromFile(newThumbFile);
  465.  
  466.   aPhotoUpload.previewFilePath = newPreviewFile.path;
  467.   aPhotoUpload.previewFileSpec = newPreviewSpec;
  468.  
  469.   aPhotoUpload.thumbFilePath = newThumbFile.path;
  470.   aPhotoUpload.thumbFileSpec = newThumbSpec;
  471.   aPhotoUpload.rotation = rotation + "";
  472.  
  473.   flock_photo.util.removeFileByPath(thumbPath);
  474.   flock_photo.util.removeFileByPath(previewPath);
  475. }
  476.  
  477. flockPhotoUploadService.prototype.rotatePhotoUpload = function (aPhotoUpload, aDegrees) {
  478.   var rotation = (parseInt(aPhotoUpload.rotation) + aDegrees) % 360;
  479.   this.regenerateThumbs(aPhotoUpload, rotation, null, null, null, null);
  480. }
  481.  
  482. flockPhotoUploadService.prototype.createPhotoUpload = function (aListener, aUrl) {
  483.   var url = aUrl;
  484.   var URIFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
  485.   var uri = URIFixup.createFixupURI(url, URIFixup.FIXUP_FLAG_NONE);
  486.  
  487.   var sourceFile = flock_getFileFromURL(url);
  488.   var sourceBufferStream = flock_getBufferredInputStream(sourceFile);
  489.   var sourceTitle = sourceFile.leafName;
  490.   var sourceFilename = url;
  491.  
  492.   var sinkFileName = this.getUniqueFname(sourceFilename);
  493.   var sinkFile = flock_getTmpFile(sinkFileName);
  494.   var sinkBufferStream = flock_getBufferredOutputStream(sinkFile);
  495.  
  496.   this.mAsyncCopier.init(sourceBufferStream, sinkBufferStream, null, true, true, FLOCK_IO_CHUNKSIZE);
  497.   aListener.onCreateStart(sourceTitle); 
  498.  
  499.   var inst = this;
  500.  
  501.   var requestObserver = {
  502.     fname: sourceTitle,
  503.     asyncCopier: this.mAsyncCopier,
  504.     url: url,
  505.     sinkFile: sinkFile,
  506.     sinkFileName: sinkFileName,
  507.     preview: false,
  508.     onStartRequest: function(aRequest, aContext) {
  509.     },
  510.     onStopRequest: function(aRequest, aContext) {
  511.       var sourcePath = this.sinkFile.path;
  512.       var thumbPath = getOtherFName(sourcePath, "thumb");
  513.       var previewPath = getOtherFName(sourcePath, "preview");
  514.       var workingPath = getOtherFName(sourcePath, "working");
  515.       flock_photo.util.resizeAsync(FLOCK_PREVIEW_SIZE, sourcePath, previewPath, this);
  516.     },
  517.     onFinished: function requestObserver_onFinished (aResult) {
  518.       debug("onFinished: " + aResult + "\n");
  519.       if (!inst._isImporting) return;
  520.       var sourcePath = this.sinkFile.path;
  521.       var thumbPath = getOtherFName(sourcePath, "thumb");
  522.       var previewPath = getOtherFName(sourcePath, "preview");
  523.       var workingFilePath = getOtherFName(sourcePath, "working");
  524.  
  525.       // if file size is zero
  526.       if (!flock_photo.util.getFileFromPath(sourcePath).fileSize) {
  527.         var error = Components.classes[FLOCK_ERROR_CONTRACTID].createInstance(Ci.flockIError);
  528.         error.errorCode = error.PHOTOSERVICE_FILE_EMPTY;
  529.         aListener.onError(error);
  530.         return;
  531.       }
  532.       
  533.       try {
  534.         if(!this.preview) {
  535.           var previewFile = flock_photo.util.getFileFromPath(previewPath);
  536.           previewFile.copyTo(null, getOtherFName(this.sinkFile.leafName, "working"));
  537.           this.preview = true;
  538.           flock_photo.util.thumbAsync(FLOCK_THUMB_SIZE, previewPath, thumbPath, this);
  539.         } else {
  540.           var thumbFile = flock_photo.util.getFileFromPath(thumbPath);
  541.           var thumbSpec = flock_getURLFromFile(thumbFile);
  542.  
  543.           var previewFile = flock_photo.util.getFileFromPath(previewPath);
  544.           var previewSpec = flock_getURLFromFile(previewFile);
  545.  
  546.           var workingFile = flock_photo.util.getFileFromPath(workingFilePath);
  547.  
  548.           var date = new Date();
  549.           
  550.           var photo = Components.classes['@flock.com/photo-upload;1'].createInstance(Ci.flockIPhotoUpload);
  551.           photo.originalFileSpec = flock_getURLFromFile(this.sinkFile);
  552.           photo.originalFilePath = sourcePath;
  553.           photo.workingFilePath = workingFilePath;
  554.           photo.thumbFileSpec = thumbSpec;
  555.           photo.thumbFilePath = thumbPath;
  556.           photo.previewFileSpec = previewSpec;
  557.           photo.previewFilePath = previewPath;
  558.           photo.privacy_use_batch = "true";
  559.           photo.is_public = "true";
  560.           photo.tags = "";
  561.           photo.notes = "";
  562.           photo.description = "";
  563.           photo.is_friend = "false";
  564.           photo.is_family = "false";
  565.           photo.title = this.fname;
  566.           photo.fileName = this.fname;
  567.           photo.state = "";
  568.           photo.rotation = "0";
  569.           photo.crop = "0,0,1,1";
  570.           photo.id = "uri:" + hex_md5(sourcePath);
  571.           photo.album = ""; // null for now
  572.           aListener.onCreateEnd(photo);
  573.         }
  574.       } catch (ex) {
  575.         debug("photoupload: " + ex + " in " + ex.fileName+" line "+ex.lineNumber + "\n");
  576.         debug("photoupload: " + ex.description + "\n");
  577.         aListener.onCreateError(this.fname);
  578.       }
  579.     }
  580.   };
  581.  
  582.   this.mAsyncCopier.asyncCopy(requestObserver, requestObserver.sinkFile);
  583. }
  584.  
  585. flockPhotoUploadService.prototype.doTransform = function (aUpload) {
  586.   var url = aUpload.originalFileSpec;
  587.   var path = aUpload.originalFilePath;
  588.  
  589.   // if there are no changes to be made do not create new copy (as it changes jpg compression), just return path
  590.   // see bug 3891
  591.  
  592.   if(flock_getBoolPref(FLOCK_PREF_PHOTO_RESIZE)==true || aUpload.crop!="0,0,1,1" || parseInt(aUpload.rotation)!=0) {
  593.     var maxDim = null;
  594.     if(flock_getBoolPref(FLOCK_PREF_PHOTO_RESIZE)==true) {
  595.       var maxDim = flock_getIntPref(FLOCK_PREF_PHOTO_RESIZE_DIMENSIONS);
  596.     }
  597.     var newFile = this.doCrop(aUpload, null, maxDim);
  598.     url = flock_getURLFromFile(newFile);
  599.  
  600.     flock_photo.util.transferExif(aUpload.originalFileSpec, url);
  601.     return url;
  602.   }
  603.  
  604.   return aUpload.originalFileSpec;
  605. }
  606.  
  607. flockPhotoUploadService.prototype.upload = function (aAPI) {
  608.   if(this.isUploading) { return; }
  609.   var inst = this;
  610.   this.api = aAPI;
  611.   this.uploadListener = {
  612.     init: function(aAPI, aKids, aSvc, aMax) {
  613.       this.api = aAPI;
  614.       this.svc = aSvc;
  615.       this.kids = aKids;
  616.       this.count = 0;
  617.       this.max = aMax;
  618.       this.imageUtils = flock_getImageUtils();
  619.       this.svc.running = true;
  620.       this.doUpload();
  621.     },
  622.     stop: function() {
  623.       this.svc.doSave();
  624.       this.svc.running = false;
  625.       // XXX andy: this isn't in the idl...
  626.       //this.api.cancelUpload();
  627.       for(var i=0;i<this.svc.listeners.length;++i) {
  628.         this.svc.listeners[i].onUploadComplete();
  629.       }
  630.     },
  631.     doUpload: function() {
  632.       if (!this.svc.running || !this.kids.hasMoreElements()) {
  633.         if(this.svc.running) this.stop();
  634.         return;
  635.       }
  636.       ++this.count;
  637.       this.uploadResource = this.kids.getNext();
  638.       var upload = this.svc.getPhotoUpload(this.uploadResource.getTarget(FLOCK_NS + "id").getValue());
  639.       var url = inst.doTransform(upload);
  640.       var params = new Array();
  641.       params.title = upload.title;
  642.       params.description = upload.description;
  643.       params.is_family = upload.is_family;
  644.       params.is_friend = upload.is_friend;
  645.       params.is_public = upload.is_public;
  646.       params.async = "1";
  647.       params.tags = upload.tags;
  648.       params.notes = upload.notes;
  649.       params.album = upload.album;
  650.       this.currentUpload = upload;
  651.       this.uploadResource.addTargetOnce(FLOCK_NS + "state", "uploading");
  652.       for (var i=0;i<this.svc.listeners.length;++i) {
  653.         this.svc.listeners[i].onUploadStart(this.currentUpload, this.count, this.max);
  654.       }
  655.       this.api.upload2(this, upload, url);// note that this doesn't use the parameter stuff
  656.     },
  657.     onUploadComplete: function(aUpload) {
  658.       this.doUpload();
  659.       for(var i=0;i<this.svc.listeners.length;++i) {
  660.         this.svc.listeners[i].onUpload(aUpload, this.count, this.max);
  661.       }
  662.       this.svc.removePhotoUpload(aUpload, false);
  663.     },
  664.     onUploadFinalized: function(aUpload, aPhoto) {
  665.     },
  666.     onError: function(aError) {
  667.       this.uploadResource.addTargetOnce(FLOCK_NS + "state", "error");
  668.       for(var i=0;i<this.svc.listeners.length;++i) {
  669.         if(this.svc.running) //don't send back error on cancelUpload
  670.           this.svc.listeners[i].onUploadError(this.currentUpload, this.count, this.max, aError);
  671.       }
  672.       this.stop();
  673.       debug("error uploading\n");
  674.     },
  675.     onProgress: function(currentProgress) {
  676.       for(var i=0;i<this.svc.listeners.length;++i) {
  677.         this.svc.listeners[i].onUploadProgress(currentProgress);
  678.       }
  679.     }
  680.   };
  681.   var kids = this.uploadRoot.getChildren();
  682.  
  683.   // XXX have to reverse the order of the child nodes
  684.   // so that the service gets the oldest photo first uploaded.
  685.   var orderedKidsEnumerator  = {
  686.     mList: Array(),
  687.     currentIndex: 0,
  688.     add : function(aItem) {
  689.       this.mList.unshift(aItem);
  690.     },
  691.     getNext : function() {
  692.       this.currentIndex++;
  693.       return this.mList[this.currentIndex-1];
  694.     },
  695.     hasMoreElements : function() {
  696.       return this.mList[this.currentIndex] != null ? true : false;
  697.     },
  698.     getChildCount: function() {
  699.       return this.mList.length;
  700.     }
  701.   }
  702.   while (kids.hasMoreElements())
  703.     orderedKidsEnumerator.add(kids.getNext());
  704.  
  705.   this.uploadListener.init(this.api, orderedKidsEnumerator, this, orderedKidsEnumerator.getChildCount());
  706. }
  707.  
  708. flockPhotoUploadService.prototype.isNewUpload = function (aUpload) {
  709.   if(aUpload.getTarget(FLOCK_NS + "id")) return false;
  710.   return true;
  711. }
  712.  
  713. flockPhotoUploadService.prototype.savePhotoUpload = function (aUpload) {
  714.   debug("flockPhotoUploadService.prototype.savePhotoUpload " + aUpload.id + "\n");
  715.   debug("flockPhotoUploadService.prototype.savePhotoUpload notes = " + aUpload.notes + "\n");
  716.   var upload = this.ds.getNode(this.getURI(aUpload.id));
  717.   upload.addTargetOnce(FLOCK_NS + "id", aUpload.id);
  718.   upload.addTargetOnce(FLOCK_NS + "originalFileSpec", aUpload.originalFileSpec);
  719.   upload.addTargetOnce(FLOCK_NS + "originalFilePath", aUpload.originalFilePath);
  720.   upload.addTargetOnce(FLOCK_NS + "workingFilePath", aUpload.workingFilePath);
  721.   upload.addTargetOnce(FLOCK_NS + "thumbFilePath", aUpload.thumbFilePath);
  722.   upload.addTargetOnce(FLOCK_NS + "thumbFileSpec", aUpload.thumbFileSpec);
  723.   upload.addTargetOnce(FLOCK_NS + "previewFilePath", aUpload.previewFilePath);
  724.   upload.addTargetOnce(FLOCK_NS + "previewFileSpec", aUpload.previewFileSpec);
  725.   upload.addTargetOnce(FLOCK_NS + "fileName", aUpload.fileName);
  726.   upload.addTargetOnce(FLOCK_NS + "title", aUpload.title);
  727.   upload.addTargetOnce(FLOCK_NS + "privacy_use_batch", aUpload.privacy_use_batch);
  728.   upload.addTargetOnce(FLOCK_NS + "is_public", aUpload.is_public);
  729.   upload.addTargetOnce(FLOCK_NS + "is_friend", aUpload.is_friend);
  730.   upload.addTargetOnce(FLOCK_NS + "is_family", aUpload.is_family);
  731.   upload.addTargetOnce(FLOCK_NS + "tags", aUpload.tags);
  732.   upload.addTargetOnce(FLOCK_NS + "notes", aUpload.notes);
  733.   upload.addTargetOnce(FLOCK_NS + "state", aUpload.state);
  734.   upload.addTargetOnce(FLOCK_NS + "description", aUpload.description);
  735.   upload.addTargetOnce(FLOCK_NS + "rotation", aUpload.rotation);
  736.   upload.addTargetOnce(FLOCK_NS + "album", aUpload.album);
  737.   upload.addTargetOnce(FLOCK_NS + "crop", aUpload.crop);
  738.   this.doSave();
  739. }
  740.  
  741. flockPhotoUploadService.prototype.addPhotoUpload = function (aReference, aUpload) {
  742.   debug("flockPhotoUploadService.prototype.addPhotoUpload " + aUpload.state + "\n");
  743.   for(var i=0;i<this.listeners.length;++i) {
  744.     this.listeners[i].onUploadAdd(null);
  745.   }
  746.  
  747.   var newUpload = this.ds.getNode(this.getURI(aUpload.id));
  748.   this.uploadRoot.removeChildForReal(newUpload);
  749.  
  750.   if(this.isNewUpload(newUpload)) {
  751.     this.savePhotoUpload(aUpload);
  752.   }
  753.   if(aReference) {
  754.     var ref = this.ds.getNode(this.getURI(aReference.id));
  755.     var index = this.uploadRoot.getChildIndex(ref);
  756.     this.uploadRoot.addChildAt(newUpload, index);
  757.   }
  758.   else {
  759.     this.uploadRoot.addChildAt(newUpload, 1);
  760.   }
  761.   this.doSave();
  762.   for(var i=0;i<this.listeners.length;++i) {
  763.     this.listeners[i].onUploadAdd(aUpload);
  764.   }
  765. }
  766.  
  767.  
  768. flockPhotoUploadService.prototype.removePhotoUpload = function (aUpload, aDontDelete) {
  769.   try {
  770.     aUpload = this.getPhotoUpload(aUpload.id);
  771.   }
  772.   catch(e) {}
  773.   var toDelete = this.ds.getNode(this.getURI(aUpload.id));
  774.   this.uploadRoot.removeChild(toDelete);
  775.   this.ds.deleteRecursive(toDelete);
  776.   this.doSave();
  777.   if(!aDontDelete) {
  778.     try {
  779.     flock_photo.util.removeFileByPath(aUpload.workingFilePath);
  780.     flock_photo.util.removeFileByPath(aUpload.originalFilePath + "_backup");
  781.     flock_photo.util.removeFileBySpec(aUpload.originalFileSpec);
  782.     flock_photo.util.removeFileBySpec(aUpload.previewFileSpec);
  783.     flock_photo.util.removeFileBySpec(aUpload.thumbFileSpec);
  784.     flock_photo.util.removeFileByPath(aUpload.originalFilePath);
  785.     flock_photo.util.removeFileByPath(aUpload.previewFilePath);
  786.     flock_photo.util.removeFileByPath(aUpload.thumbFilePath);
  787.     } catch(e) {
  788.     }
  789.   }
  790.   for(var i=0;i<this.listeners.length;++i) {
  791.     this.listeners[i].onUploadRemove();
  792.   }
  793. }
  794.  
  795. flockPhotoUploadService.prototype.reorderPhotoUpload = function (aReference, aUpload) {
  796.   this.addPhotoUpload();
  797.   return;
  798. }
  799.  
  800. flockPhotoUploadService.prototype.getPhotoUpload = function (aURI) {
  801.   var upload = this.ds.getNode(this.getURI(aURI));
  802.   var rval = new Object();
  803.   rval.id = upload.getTarget(FLOCK_NS + "id").getValue();
  804.   rval.originalFileSpec = upload.getTarget(FLOCK_NS + "originalFileSpec").getValue();
  805.   rval.originalFilePath = upload.getTarget(FLOCK_NS + "originalFilePath").getValue();
  806.   rval.workingFilePath = upload.getTarget(FLOCK_NS + "workingFilePath").getValue();
  807.   rval.thumbFileSpec = upload.getTarget(FLOCK_NS + "thumbFileSpec").getValue();
  808.   rval.thumbFilePath = upload.getTarget(FLOCK_NS + "thumbFilePath").getValue();
  809.   rval.previewFileSpec = upload.getTarget(FLOCK_NS + "previewFileSpec").getValue();
  810.   rval.previewFilePath = upload.getTarget(FLOCK_NS + "previewFilePath").getValue();
  811.   rval.fileName = upload.getTarget(FLOCK_NS + "fileName").getValue();
  812.   rval.title = upload.getTarget(FLOCK_NS + "title").getValue();
  813.   rval.privacy_use_batch = upload.getTarget(FLOCK_NS + "privacy_use_batch").getValue();
  814.   rval.is_public = upload.getTarget(FLOCK_NS + "is_public").getValue();
  815.   rval.is_friend = upload.getTarget(FLOCK_NS + "is_friend").getValue();
  816.   rval.is_family = upload.getTarget(FLOCK_NS + "is_family").getValue();
  817.   rval.tags = upload.getTarget(FLOCK_NS + "tags").getValue();
  818.   rval.notes = upload.getTarget(FLOCK_NS + "notes").getValue();
  819.   rval.state = upload.getTarget(FLOCK_NS + "state").getValue();
  820.   rval.description = upload.getTarget(FLOCK_NS + "description").getValue();
  821.   rval.rotation = upload.getTarget(FLOCK_NS + "rotation").getValue();
  822.   rval.album = upload.getTarget(FLOCK_NS + "album").getValue();
  823.   rval.crop = upload.getTarget(FLOCK_NS + "crop").getValue();
  824.   return rval;
  825. }
  826.  
  827. flockPhotoUploadService.prototype.cleanPhotoUploadCache = function () {
  828.   debug("Clean the cache.\n");
  829.   try {
  830.     var sinkFile = Cc["@mozilla.org/file/directory_service;1"]
  831.       .getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
  832.     sinkFile.append("flock_imagecache");
  833.     if (sinkFile.exists()) {
  834.       debug("Image cache found\n");
  835.       var files = sinkFile.directoryEntries;
  836.       while (files.hasMoreElements()) {
  837.         var file = files.getNext().QueryInterface(Ci.nsIFile);
  838.         try {
  839.           file.remove(false);
  840.         } catch (e) {
  841.           debug("could not delete file: " + file.path + " exception: " + e + "\n");
  842.         }
  843.       }
  844.     } else {
  845.       debug("Image cache not found\n");
  846.     }
  847.   } catch (e) {
  848.     debug("exception cleaning the cache: " + e + "\n");
  849.   }
  850. }
  851.  
  852. // nsIObserver
  853. flockPhotoUploadService.prototype.observe =
  854. function PUS_observe (subject, topic, state)
  855. {
  856.   switch (topic) {
  857.     case 'flock-data-ready':
  858.       var obs = Cc["@mozilla.org/observer-service;1"]
  859.         .getService(Ci.nsIObserverService);
  860.       obs.removeObserver(this, 'flock-data-ready');
  861.       this.init();
  862.       return;
  863.   }
  864. }
  865.  
  866. loadLibraryFromSpec('chrome://browser/content/flock/common/flocksafe.js');
  867. loadLibraryFromSpec('chrome://browser/content/flock/common/md5.js');
  868. loadLibraryFromSpec('chrome://browser/content/flock/contrib/rdfds.js');
  869. loadLibraryFromSpec('chrome://browser/content/flock/photo/photoAPI.js');
  870. loadLibraryFromSpec('chrome://browser/content/flock/photo/photo.js');
  871.  
  872. function loadLibraryFromSpec(aSpec) {
  873.   var loader = Cc['@mozilla.org/moz/jssubscript-loader;1'].getService(Ci.mozIJSSubScriptLoader);
  874.  
  875.   loader.loadSubScript(aSpec);
  876. }
  877.  
  878. flockPhotoUploadService.prototype.URI = 'rdf:flock-photo-uploads';
  879. flockPhotoUploadService.prototype.GetSource = function (aProperty, aTarget, aTruthValue) {
  880.   return this.dataSource.GetSource (aProperty, aTarget, aTruthValue);
  881. }
  882. flockPhotoUploadService.prototype.GetSources = function (aProperty, aTarget, aTruthValue) {
  883.   return this.dataSource.GetSources (aProperty, aTarget, aTruthValue);
  884. }
  885. flockPhotoUploadService.prototype.GetTarget = function (aSource, aProperty, aTruthValue) {
  886.   return this.dataSource.GetTarget (aSource, aProperty, aTruthValue);
  887. }
  888. flockPhotoUploadService.prototype.GetTargets = function (aSource, aProperty, aTruthValue) {
  889.   return this.dataSource.GetTargets (aSource, aProperty, aTruthValue);
  890. }
  891. flockPhotoUploadService.prototype.Assert = function (aSource, aProperty, aTarget, aTruthValue) {
  892.   this.dataSource.Assert (aSource, aProperty, aTarget, aTruthValue);
  893. }
  894. flockPhotoUploadService.prototype.Unassert = function (aSource, aProperty, aTarget) {
  895.   this.dataSource.Unassert (aSource, aProperty, aTarget);
  896. }
  897. flockPhotoUploadService.prototype.Change = function (aSource, aProperty, aOldTarget, aNewTarget) {
  898.   this.dataSource.Change (aSource, aProperty, aOldTarget, aNewTarget);
  899. }
  900. flockPhotoUploadService.prototype.Move = function (aOldSource, aNewSource, aProperty, aTarget) {
  901.   this.dataSource.Move (aOldSource, aNewSource, aProperty, aTarget);
  902. }
  903. flockPhotoUploadService.prototype.HasAssertion = function (aSource, aProperty, aTarget, aTruthValue) {
  904.   return this.dataSource.HasAssertion (aSource, aProperty, aTarget, aTruthValue);
  905. }
  906. flockPhotoUploadService.prototype.AddObserver = function (aObserver) {
  907.   this.dataSource.AddObserver (aObserver);
  908. }
  909. flockPhotoUploadService.prototype.RemoveObserver = function (aObserver) {
  910.   this.dataSource.RemoveObserver (aObserver);
  911. }
  912. flockPhotoUploadService.prototype.ArcLabelsIn = function (aNode) {
  913.   return this.dataSource.ArcLabelsIn (aNode);
  914. }
  915. flockPhotoUploadService.prototype.ArcLabelsOut = function (aNode) {
  916.   return this.dataSource.ArcLabelsOut (aNode);
  917. }
  918. flockPhotoUploadService.prototype.GetAllResources = function () {
  919.   return this.dataSource.GetAllResources ();
  920. }
  921. flockPhotoUploadService.prototype.IsCommandEnabled = function (aSources, aCommand, aArguments) {
  922.   return this.dataSource.IsCommandEnabled (aSources, aCommand, aArguments);
  923. }
  924. flockPhotoUploadService.prototype.DoCommand = function (aSources, aCommand, aArguments) {
  925.   this.dataSource.DoCommand (aSources, aCommand, aArguments);
  926. }
  927. flockPhotoUploadService.prototype.GetAllCmds = function (aSource) {
  928.   return this.dataSource.GetAllCmds (aSource);
  929. }
  930. flockPhotoUploadService.prototype.hasArcIn = function (aNode, aArc) {
  931.   return this.dataSource.hasArcIn (aNode, aArc);
  932. }
  933. flockPhotoUploadService.prototype.hasArcOut = function (aNode, aArc) {
  934.   return this.dataSource.hasArcOut (aNode, aArc);
  935. }
  936. flockPhotoUploadService.prototype.beginUpdateBatch = function () {
  937.   this.dataSource.beginUpdateBatch ();
  938. }
  939. flockPhotoUploadService.prototype.endUpdateBatch = function () {
  940.   this.dataSource.endUpdateBatch ();
  941. }
  942. flockPhotoUploadService.prototype.Flush = function () {
  943.   this.dataSource.Flush ();
  944. }
  945. flockPhotoUploadService.prototype.FlushTo = function (aURI) {
  946.   this.dataSource.FlushTo (aURI);
  947. }
  948. flockPhotoUploadService.prototype.Init = function (aURI) {
  949.   this.dataSource.Init (aURI);
  950. }
  951. flockPhotoUploadService.prototype.Refresh = function (aBlocking) {
  952.   this.dataSource.Refresh (aBlocking);
  953. }
  954.  
  955. flockPhotoUploadService.prototype.flags = Ci.nsIClassInfo.SINGLETON;
  956. flockPhotoUploadService.prototype.classDescription = "Flock Photo Service";
  957. flockPhotoUploadService.prototype.getInterfaces = function (count) {
  958.   var interfaceList = [Ci.flockIPhotoUploadService, Ci.nsIClassInfo, Ci.nsIObserver];
  959.   count.value = interfaceList.length;
  960.   return interfaceList;
  961. }
  962. flockPhotoUploadService.prototype.getHelperForLanguage = function (count) {return null;}
  963.  
  964. // the nsISupports implementation
  965. flockPhotoUploadService.prototype.QueryInterface =
  966. function (iid) {
  967.   if (!iid.equals(Ci.flockIPhotoUploadService) &&
  968.     !iid.equals(Ci.nsIRDFDataSource) &&
  969.     !iid.equals(Ci.nsIClassInfo) &&
  970.     !iid.equals(Ci.nsIObserver) &&
  971.     !iid.equals(Ci.nsISupports))
  972.     throw Cr.NS_ERROR_NO_INTERFACE;
  973.   if (iid.equals(Ci.nsIRDFDataSource) && !this.dataSourceSetup) {
  974.   }
  975.   return this;
  976. }
  977.  
  978. // Module implementation
  979. var Module = new Object();
  980.  
  981. Module.registerSelf =
  982. function (compMgr, fileSpec, location, type)
  983. {
  984.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  985.  
  986.   compMgr.registerFactoryLocation(FLOCK_PHOTO_UPLOAD_CID,
  987.                                   "Flock Photo JS Component",
  988.                                   FLOCK_PHOTO_UPLOADS_CONTRACTID,
  989.                                   fileSpec,
  990.                                   location,
  991.                                   type);
  992.   var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  993.   categoryManager.addCategoryEntry("flock-startup", "Flock Photo Upload Service", "service," + FLOCK_PHOTO_UPLOADS_CONTRACTID, true, true);
  994. }
  995.  
  996. Module.getClassObject =
  997. function (compMgr, cid, iid) {
  998.   if (!cid.equals(FLOCK_PHOTO_UPLOAD_CID))
  999.     throw Cr.NS_ERROR_NO_INTERFACE;
  1000.  
  1001.   if (!iid.equals(Ci.nsIFactory))
  1002.     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  1003.  
  1004.   return PhotoUploadServiceFactory;
  1005. }
  1006.  
  1007. Module.canUnload =
  1008. function(compMgr) {
  1009.   return true;
  1010. }
  1011.  
  1012. /* factory object */
  1013. var PhotoUploadServiceFactory = new Object();
  1014.  
  1015. PhotoUploadServiceFactory.createInstance =
  1016. function (outer, iid) {
  1017.   if (outer != null)
  1018.     throw Components.results.NS_ERROR_NO_AGGREGATION;
  1019.  
  1020.   return (new flockPhotoUploadService()).QueryInterface(iid);
  1021. }
  1022.  
  1023. /* entrypoint */
  1024. function NSGetModule(compMgr, fileSpec) {
  1025.   return Module;
  1026. }
  1027.